package org.starti.persistence.hibernate;

import org.hibernate.*;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Transactional;
import org.starti.persistence.PersistenceException;
import org.starti.persistence.PersistenceService;
import org.starti.utils.Utils;
import wicket.extensions.markup.html.repeater.util.SortParam;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author <a href="mailto:claudiu.dumitrescu@gmail.com">Claudiu Dumitrescu</a>
 */
public class HibernatePersistenceService extends HibernateDaoSupport implements
        PersistenceService, ApplicationContextAware {
    private ApplicationContext appContext;

    /*
    * (non-Javadoc)
    *
    *
    */
    @Transactional
    public <T> T getInstance(Class<T> type, Serializable id) {
        return (T) getHibernateTemplate().get(Utils.checkForCGLIB(type), id);
    }

    /*
     * (non-Javadoc)
     * 
     *
     */
    @Transactional
    public List getAllInstances(Class type, SortParam... sortParams) {
        Session session = getSessionFactory().getCurrentSession();
        Criteria searchCriteria = session.createCriteria(Utils.checkForCGLIB(type));
        for (SortParam sortParam : sortParams) {
            if (sortParam.isAscending()) {
                searchCriteria.addOrder(Order.asc(sortParam.getProperty()));
            } else {
                searchCriteria.addOrder(Order.desc(sortParam.getProperty()));
            }
        }
        return searchCriteria.list();
    }

    /*
     * (non-Javadoc)
     * 
     */
    @Transactional
    public <T> T save(T instance) {
        try {
            return (T) getHibernateTemplate().merge(instance);
        }
        catch (DataAccessException dex) {
            throw new PersistenceException(dex);
        }

    }

    @Transactional
    public void remove(Object instance) {
        // merge first to avoid NonUniqueObjectException
        getHibernateTemplate().delete(getHibernateTemplate().merge(instance));
    }

    /*
     * (non-Javadoc)
     * 
     *
     */
    @Transactional
    public List getInstances(final DetachedCriteria criteria) {
        // TODO Auto-generated method stub
        return (List) getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session)
                    throws HibernateException, SQLException {
                criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
                return criteria.getExecutableCriteria(session).list();
            }
        }, true);
    }

    public List getAllTypes() {
        ArrayList allTypes = new ArrayList();
        for (Iterator iter = getSessionFactory().getAllClassMetadata().values()
                .iterator(); iter.hasNext();) {
            ClassMetadata classMeta = (ClassMetadata) iter.next();
            allTypes.add(classMeta.getMappedClass(EntityMode.POJO));
        }
        return allTypes;
    }

    @Transactional
    public void reattach(Object model) {
        getSession().lock(model, LockMode.NONE);

    }

    public List getInstances(Object example) {
        return null;
    }


    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
        this.appContext = arg0;

    }
}
